/*
 * @Author: Wangjun
 * @Date: 2021-08-20 11:00:46
 * @LastEditTime: 2024-05-08 15:06:31
 * @LastEditors: wangjun haodreams@163.com
 * @Description:struct 和 xlsx 转换
 * @FilePath: \golib\structdetail\struct_detail.go
 * hnxr
 */
package memtable

import (
	"errors"
	"reflect"
)

const (
	TypeNumber = 0
	TypeString = 1
	TypeUnknow = -1
)

// 列信息
type Column struct {
	//reflect.StructField
	Title    string //tag 名称
	Name     string
	FullName string
	RKind    reflect.Kind
	Kind     int   // 0:num 1:string -1:其他
	Index    []int //全路径
}

// struct 信息
type StructDetail struct {
	Name    string
	Columns []*Column
	err     error //错误消息

	tagKey string //要读取tag key
}

/**
 * @description: 新的映射关系
 * @param {interface{}} arrayObject	struct 数组
 * @param {string} tagKey  需要识别的tag字段
 * @return {*}
 */
func NewStructDetail(arrayObject interface{}, tagKey string) (sd *StructDetail) {
	sd = new(StructDetail)
	sd.tagKey = tagKey
	sd.Init(arrayObject)
	return
}

/**
 * @description: 初始化数据
 * @param {interface{}} arrayObject
 * @return {*}
 */
func (m *StructDetail) Init(arrayObject interface{}) {
	//关联key的索引
	t := reflect.TypeOf(arrayObject)
	if !(t.Kind() == reflect.Slice || t.Kind() == reflect.Array || t.Kind() == reflect.Ptr) {
		m.err = errors.New("参数错误，参数必须是结构体切片或者数组")
		return
	}
	t = t.Elem()
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	if t.Kind() != reflect.Struct {
		m.err = errors.New("数组的内容不是结构体")
		return
	}
	m.Name = t.Name()
	m.initColumn("", nil, t)
}

/**
 * @description: 初始化列
 * @param {string} prefix 前缀 上一路径名
 * @param {[]int} paths 上一路径ID
 * @param {reflect.Type} t 类型
 * @return {*}
 */
func (m *StructDetail) initColumn(prefix string, paths []int, t reflect.Type) {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	for i := 0; i < t.NumField(); i++ {
		newPaths := make([]int, len(paths))
		copy(newPaths, paths)
		newPaths = append(newPaths, i)
		fieldType := t.Field(i)

		typ := fieldType.Type

		if typ.Kind() == reflect.Ptr {
			typ = typ.Elem()
		}

		title := ""
		if m.tagKey != "" {
			title = fieldType.Tag.Get(m.tagKey)
			if title == "-" {
				continue
			}
		}

		fullName := prefix + fieldType.Name
		if fieldType.Anonymous {
			if typ.Kind() == reflect.Struct {
				m.initColumn(prefix, newPaths, typ)
				continue
			}
		} else {
			if typ.Kind() == reflect.Struct {
				if fieldType.Name == "" {
					continue
				}
				if fieldType.Name[0] >= 'A' && fieldType.Name[0] <= 'Z' {
					m.initColumn(fullName+".", newPaths, typ)
				}
			} else {
				if !(fieldType.Name[0] >= 'A' && fieldType.Name[0] <= 'Z') {
					continue
				}
			}
		}

		col := new(Column)
		col.Name = fieldType.Name
		col.Index = newPaths
		col.RKind = fieldType.Type.Kind()
		switch typ.Kind() {
		case reflect.String:
			col.Kind = TypeString
		case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
			col.Kind = TypeNumber
		default:
			col.Kind = TypeUnknow
		}

		if title == "" {
			title = fullName
		}
		col.Title = title
		col.FullName = fullName
		m.Columns = append(m.Columns, col)
	}
}
